# -*- coding: utf-8 -*-
# @Time    : 2016/9/27
from sympy import solve, expand, Eq, sympify

from mathsolver.functions.hanshu.helper import check_func
from mathsolver.functions.base import (BaseFunction, BaseValue, BaseEq, BasePoint, BaseFunc, BaseZhiXian, BasePieceFunc,
                                       new_latex)
from mathsolver.functions.daoshu.qiexianxielu import DaoShuQieXianXieLv001
from mathsolver.functions.zhixian.fangcheng import SolveFangChengDianXie
from mathsolver.functions.daoshu.qiexianfangcheng_qiucan import is_zhixian


def huajian_eq(eq):
    l, r = eq.sympify()
    return BaseEq([expand(l - r), 0])


class QieXianFangCheng001(BaseFunction):
    """求在一点的切线方程"""

    def solver(self, *args):
        zhixian_yuanzhui_func, qiedian = args

        if is_zhixian(zhixian_yuanzhui_func):
            func = check_func(zhixian_yuanzhui_func)
        else:
            func = zhixian_yuanzhui_func
            assert isinstance(qiedian, BasePoint), "输入必须为切点"

        # 输入可能是 BaseEq 或者 BasePoint 格式
        if isinstance(qiedian, BaseEq):
            _, x1 = qiedian.sympify()
        else:
            x1 = qiedian.sympify()[0]

        # 补充： 如果是分段函数，求出点所在分段表达式
        if isinstance(func, BasePieceFunc):
            for expr, qujian in func.expression:
                if x1 in qujian:
                    val = func.value
                    val['expression'] = expr
                    func = BaseFunc(val)
                    self.steps.append(["", "%s ∈ %s, 所在区间函数表达式为 %s" % (x1, qujian, func.printing())])
                    break

        if isinstance(qiedian, BaseEq):
            x2 = func.expression.subs(func.var, x1)
            point = BasePoint({"name": "", "value": [x1, x2]})
        else:
            x2 = qiedian.sympify()[1]
            point = qiedian

        # 求切线的斜率
        qiuxielu = DaoShuQieXianXieLv001().solver(func, point)
        xielu = qiuxielu.output[0]
        self.steps.extend(qiuxielu.steps)

        # 已知切点直接求出切线方程

        # 如果不知道切点坐标，需要先求切点坐标再求切线方程
        if x2.is_Function:
            x2 = func.expression.subs(func.var, x1)

        qiufangcheng = SolveFangChengDianXie().solver(BasePoint({"name": "", "value": [x1, x2]}), xielu)
        fangcheng = qiufangcheng.output[0]
        self.steps.extend(qiufangcheng.steps)

        # 化简方程
        fangcheng_simplified = huajian_eq(fangcheng)
        fangcheng_simplified.intermediate = False
        self.steps.append(["所以切线方程为 {}".format(fangcheng_simplified.printing()), ""])

        fangcheng_simplified.intermediate = False
        self.output.append(BaseZhiXian({"name": "", "value": fangcheng_simplified.value}))
        self.label.add("求在一点的切线方程")
        return self


class QieXianFangCheng002(BaseFunction):
    """求过一点的切线方程"""

    def solver(self, *args):
        zhixian_yuanzhui_func, point = args[0], args[1]

        if is_zhixian(zhixian_yuanzhui_func):
            func = check_func(zhixian_yuanzhui_func)
        else:
            func = zhixian_yuanzhui_func

        x1, x2 = point.sympify()

        # 补充： 如果是分段函数，求出点所在分段表达式
        if isinstance(func, BasePieceFunc):
            for expr, qujian in func.expression:
                if x1 in qujian:
                    val = func.value
                    val['expression'] = expr
                    func = BaseFunc(val)
                    self.steps.append(["", "%s ∈ %s, 所在区间函数表达式为 %s" % (x1, qujian, func.printing())])
                    break

        # 1. 若点为切点，求在一点的切线方程
        self.steps.append(["若 {} 为切点".format(point.printing()), ""])
        qiuqiexian = QieXianFangCheng001().solver(func, point)
        fangcheng1 = huajian_eq(qiuqiexian.output[0])
        self.steps.extend(qiuqiexian.steps[1:])

        # 2. 若点不是切点，设出切点坐标
        qiedian = BasePoint({'name': '', 'value': ['m', 'n']})
        m, n = qiedian.sympify()
        self.steps.append(["若 {} 不是切点".format(point.printing()), "设切点坐标为 {}".format(qiedian.printing())])

        # 2.1 求切线的斜率，切线斜率 = 点斜式斜率
        qiuxielu = DaoShuQieXianXieLv001().solver(func, qiedian)
        xielu = qiuxielu.output[0].sympify()
        self.steps.extend(qiuxielu.steps)
        eq1 = Eq(xielu, (n - x2) / (m - x1))

        # 2.2 切点 (m, n) 在曲线/直线上
        if is_zhixian(zhixian_yuanzhui_func):
            eq2 = Eq(func.expression.subs(func.var, m), n)
        else:
            from sympy.abc import x, y
            yz_left_og, yz_right_og = sympify(zhixian_yuanzhui_func.value)
            to_subs = {x: m, y: n}
            eq2 = Eq(yz_left_og.subs(to_subs), yz_right_og.subs(to_subs))

        # 2.3 联立方程求得未知数
        self.steps.append(["", "则：%s，%s" % (new_latex(eq1), new_latex(eq2))])

        result = solve([eq1, eq2], [m, n], dict=True)[0]
        self.steps.append(["解得 " + ", ".join(["{} = {}".format(key, value) for key, value in result.items()]), ""])

        m_value, n_value = result[m], result[n]
        xielu_value = (n_value - x2) / (m_value - x1)

        # 2.4 点斜式求出切线方程
        qiufangcheng = SolveFangChengDianXie().solver(BasePoint({"name": "", "value": [m_value, n_value]}), BaseValue(xielu_value))
        fangcheng2 = huajian_eq(qiufangcheng.output[0])
        self.steps.extend(qiufangcheng.steps)
        self.steps.append(["所以切线方程为 {} 或者 {}".format(fangcheng1.printing(), fangcheng2.printing()), ""])

        fangcheng1.intermediate = False
        fangcheng2.intermediate = False

        self.output.extend([fangcheng1, fangcheng2])
        self.label.add("求过一点的切线方程")

        return self


if __name__ == '__main__':
    pass
